package com.example.demo.filter;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.Base64;

/**
 * @author ListJiang
 * @since 2021/10/19
 * description
 */
public class LoginFilter extends OncePerRequestFilter {
    public static final String TOKEN_KEY = "token-key:";
    private static final Log log = LogFactory.getLog(LoginFilter.class);

    private final AntPathRequestMatcher antPathRequestMatcher = new AntPathRequestMatcher("/login");
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    private final InMemoryUserDetailsManager userDetailsService;
    private final RedisTemplate redisTemplate;


    public LoginFilter(InMemoryUserDetailsManager userDetailsService, RedisTemplate redisTemplate) {
        this.userDetailsService = userDetailsService;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {

        if (antPathRequestMatcher.matches(request)) {
            log.info("自定义请求头传user，password登录...");

            String username = request.getHeader("username");
            String password = request.getHeader("password");

            // 自定义登录逻辑
            if (StringUtils.hasText(username) && StringUtils.hasText(password) && userDetailsService.userExists(username)) {
                // 去掉token前缀(Bearer )，拿到真实token
                String authToken =
                        new String(Base64.getEncoder().encode((username + ":" + password).getBytes(StandardCharsets.UTF_8)));
                // 查询用户
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);

                if (userDetails != null && PasswordEncoderFactories.createDelegatingPasswordEncoder().matches(password, userDetails.getPassword())) {
                    // 后面的拦截器里面会用到 grantedAuthorities 方法
                    UsernamePasswordAuthenticationToken authentication =
                            new UsernamePasswordAuthenticationToken(userDetails, authToken,
                                    userDetails.getAuthorities());

                    // 将authentication信息放入到security上下文对象中
                    redisTemplate.opsForValue().set(TOKEN_KEY + authToken, authentication, Duration.ofMinutes(30));
                    SecurityContextHolder.getContext().setAuthentication(authentication);

                    log.info("自定义请求头传user，password登录成功, user : " + userDetails.getUsername());
                    this.redirectStrategy.sendRedirect(request, response, "/index");
                } else {
                    chain.doFilter(request, response);
                }
            } else {
                chain.doFilter(request, response);
            }
        } else {
            chain.doFilter(request, response);
        }

    }
}